package com.server.Netty.handler;

import com.proto_java.proto_main.ProtobufPackage;
import com.server.config.WebSocketTaskScheduler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;

@Slf4j
public class FrameDataHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame>{

    // 存放所有的连接对象，方便后续广播
    // 使用ConcurrentHashMap 保证多线程下不会冲突(这个是线程安全的map)
    public static Map<Long,ChannelHandlerContext> allConnect=new ConcurrentHashMap<>();

    // 所有连接的uid映射表，配合上面的使用可以使断开连接时删除map内容的时间复杂度为O（1）
    // 这里其实就只是把上面内容的键值反过来放而已
    public static Map<ChannelHandlerContext,Long> connectTable=new ConcurrentHashMap<>();


    /**
     * 这里只需要获取对应回调并执行即可
     * 具体逻辑已经在初始化的时候被WebSocketTaskScheduler根据Event注解识别并添加了TaskHandler中的方法处理
     * @param ctx 通道上下文
     * @param binaryWebSocketFrame 原始数据包
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame binaryWebSocketFrame) {
        try {
            // 先转换为外壳类型
            ProtobufPackage.PackageInfo info = ProtobufPackage.PackageInfo.parseFrom(
                    binaryWebSocketFrame.content().nioBuffer());
            // 通过初始化时注册的回调方法Id获取函数接口对象
            BiConsumer<ProtobufPackage.PackageInfo,ChannelHandlerContext> handler =
                    WebSocketTaskScheduler.pool.get(info.getCallbackId());
            // 传入完整数据包和通道上下文,执行对应回调方法并在回调方法内返回结果给前端
            handler.accept(info,ctx);
        } catch (Exception e) {
            log.error("事件处理异常",e);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress()+": 连接成功！");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 删除断开的连接
        allConnect.remove(connectTable.get(ctx));
        connectTable.remove(ctx);
        log.info(ctx.channel().remoteAddress()+": 连接已断开");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info(ctx.channel().remoteAddress()+": 发生异常:"+cause.getMessage());
    }
}
